import io
import logging
import math
from typing import Optional, Union

import barcode
import win32print
from barcode.writer import ImageWriter
from PIL import Image, ImageDraw, ImageFont

from src.utils.resources_path import resource_path

logger = logging.getLogger(__name__)


def calculate_x_position(paper_width_mm, image_width_px, dpi=203, alignment="center"):
    """
    计算指定DPI下的图像X坐标

    参数:
        paper_width_mm: 纸张宽度(mm)
        image_width_px: 图像宽度(像素)
        dpi: 打印机分辨率(DPI)
        alignment: 对齐方式 ('left', 'center', 'right')

    返回:
        X坐标(像素)
    """
    # 毫米转英寸再转像素
    paper_width_px = int((paper_width_mm / 25.4) * dpi)

    if alignment == "right":
        return 0
    elif alignment == "center":
        return (paper_width_px - image_width_px) // 2
    elif alignment == "left":
        return paper_width_px - image_width_px
    else:
        raise ValueError("对齐方式必须是 'left', 'center', 或 'right'")


def calculate_y_position(paper_height_mm, image_heigth_px, dpi=200, alignment="center"):
    """
    计算指定DPI下的图像y坐标

    参数:
        paper_width_mm: 纸张宽度(mm)
        image_width_px: 图像宽度(像素)
        dpi: 打印机分辨率(DPI)
        alignment: 对齐方式 ('top', 'center', 'bottom')

    返回:
        X坐标(像素)
    """
    # 毫米转英寸再转像素
    paper_height_px = int((paper_height_mm / 25.4) * dpi)

    if alignment == "bottom":
        return 0
    elif alignment == "center":
        return (paper_height_px - image_heigth_px) // 2
    elif alignment == "top":
        return paper_height_px - image_heigth_px
    else:
        raise ValueError("对齐方式必须是 'top', 'center', 或 'bottom'")


def print_tspl(tspl, printer_name=None, doc_name="TSPL Print Job"):
    """以RAW模式打印TSPL指令到指定打印机"""
    # 获取默认打印机或指定打印机
    if not printer_name:
        printer_name = win32print.GetDefaultPrinter()

    try:
        # 打开打印机
        h_printer = win32print.OpenPrinter(printer_name)

        # 设置打印文档信息
        doc_info = (doc_name, None, "RAW")

        # 开始打印作业
        win32print.StartDocPrinter(h_printer, 1, doc_info)
        win32print.StartPagePrinter(h_printer)

        win32print.WritePrinter(h_printer, tspl)

        # 结束打印作业
        win32print.EndPagePrinter(h_printer)
        win32print.EndDocPrinter(h_printer)
        win32print.ClosePrinter(h_printer)

    except Exception as e:
        logger.error(f"打印错误: {e}")
        raise e


def bmp_to_tspl_bitmap(bmp: Union[io.BytesIO, str], x=0, y=0, mode=0):
    """
    将BMP图像转换为TSPL BITMAP指令（包含二进制数据）
    :param bmp_path: BMP图像路径
    :param x: 图像X坐标
    :param y: 图像Y坐标
    :param mode: 绘制模式（0:覆盖, 1:或, 2:异或）
    :return: TSPL指令字节数组
    """
    # 打开图像并转换为单色位图
    with Image.open(bmp) as img:
        img = img.transpose(Image.FLIP_LEFT_RIGHT)
        img = img.transpose(Image.FLIP_TOP_BOTTOM)
        # 转换为灰度图
        img_gray = img.convert("L")
        # 转换为单色位图（阈值128）
        img_bw = img_gray.point(lambda p: 0 if p < 128 else 255, "1")

        width_pixels, height_pixels = img_bw.size
        # 计算宽度（字节数，向上取整）
        width_bytes = math.ceil(width_pixels / 8)
        total_width = width_bytes * 8
        # 初始化数据列表
        bitmap_data = []

        # 逐行处理像素
        for y_coord in range(height_pixels):
            byte = 0
            bit_position = 7  # 从高位开始（128）

            for x_coord in range(total_width):
                # 获取像素值（0为黑，255为白）
                if x_coord < width_pixels:
                    pixel = img_bw.getpixel((x_coord, y_coord))
                else:
                    pixel = 255

                if pixel == 255:
                    byte |= 1 << bit_position

                bit_position -= 1

                # 每8个像素组成一个字节
                if bit_position < 0 or x_coord == total_width - 1:
                    bitmap_data.append(byte)
                    byte = 0
                    bit_position = 7

    # 生成TSPL指令的各个部分
    command_parts = [
        f"BITMAP {x},{y},{width_bytes},{height_pixels},{mode},".encode("ascii"),
        bytes(bitmap_data),
    ]

    return b"".join(command_parts)


def generate_bmp(
    name,
    receipt_type,
    student_id,
    output_path: Optional[str] = None,
    width=400,
    height=350,
    dpi=203,
    name_font_size_percent=0.15,
    receipt_font_size_percent=0.15,
    barcode_height_percent=0.55,
    margin_percent=0.03,
    code_moudle_width=0.3,
    code_moudle_height_percent=0.7,
    code_font_size_percent=0.225,
    code_text_distance_percent=0.3,
):
    """
    生成包含姓名、回执类型、条形码（含下方学号）的BMP图片
    """
    image_buffer = io.BytesIO()
    # 创建二值图像（1位模式，黑白）
    img = Image.new("1", (width, height), 1)  # 1=白色背景
    draw = ImageDraw.Draw(img)

    # 计算各元素大小（适当增加高度以容纳学号文本）
    name_font_size = int(height * name_font_size_percent)
    receipt_font_size = int(height * receipt_font_size_percent)
    barcode_height = int(height * barcode_height_percent)

    margin = int(height * margin_percent)

    # 加载中文字体（确保中文正常显示）
    try:
        name_font = ImageFont.truetype(
            resource_path("simhei.ttf"), name_font_size
        )  # 姓名字体
        receipt_font = ImageFont.truetype(
            resource_path("simhei.ttf"), receipt_font_size
        )  # 回执字体
    except OSError:
        #  fallback到默认字体
        name_font = ImageFont.load_default()
        receipt_font = ImageFont.load_default()
        logger.warning("警告：未找到中文字体，可能影响显示效果")

    # 1. 绘制姓名（水平居中）
    name_bbox = draw.textbbox((0, 0), name, font=name_font)
    name_x = (width - (name_bbox[2] - name_bbox[0])) // 2
    name_y = margin
    draw.text((name_x, name_y), name, font=name_font, fill=0)  # 0=黑色

    # 2. 绘制回执类型（水平居中）
    receipt_bbox = draw.textbbox((0, 0), receipt_type, font=receipt_font)
    receipt_x = (width - (receipt_bbox[2] - receipt_bbox[0])) // 2
    receipt_y = name_y + name_font_size + margin
    draw.text((receipt_x, receipt_y), receipt_type, font=receipt_font, fill=0)

    # 3. 生成条形码（Code128）并绘制
    barcode_x = int(width * 0.05)
    barcode_width = width - 2 * barcode_x
    barcode_y = receipt_y + receipt_font_size + margin

    # 创建内存缓冲区
    buffer = io.BytesIO()

    # 生成条形码并写入内存
    if not isinstance(student_id, str):
        student_id = str(student_id)

    try:
        code128 = barcode.get("code128", student_id, writer=ImageWriter(mode="1"))
        code128.write(
            buffer,
            options={
                "module_width": code_moudle_width,  # 条形码最小单元宽度
                "module_height": (barcode_height * code_moudle_height_percent)
                / dpi
                * 25.4,  # 条形码高度
                "quiet_zone": 1,  # 条形码边缘空白
                "font_size": ((barcode_height * code_font_size_percent) / dpi * 25.4)
                / 0.35,
                "text_distance": (barcode_height * code_text_distance_percent)
                / dpi
                * 25.4,
                "background": "white",
                "foreground": "black",
                "dpi": dpi,
                "font_path": resource_path("simhei.ttf"),
            },
        )
    except Exception as e:
        logger.error(f"生成条形码失败：{student_id}, 错误信息：{e}")
        raise e

    # 重置缓冲区指针到开始位置
    buffer.seek(0)

    # 直接从内存加载条形码图像
    barcode_img = Image.open(buffer).convert("L")  # 转为灰度图
    # barcode_img.show()
    barcode_img = barcode_img.resize(
        (barcode_width, barcode_height), Image.Resampling.LANCZOS
    )

    barcode_img = barcode_img.point(
        lambda x: 0 if x < 128 else 255, mode="1"
    )  # 转为二值图（修正为0和255）
    img.paste(barcode_img, (barcode_x, barcode_y))

    # 保存图像到内存缓冲区
    img.save(image_buffer, format="BMP", dpi=(dpi, dpi))
    # img.show()
    # 将文件指针重置到开头
    image_buffer.seek(0)

    # 如果指定了输出路径，也保存到文件
    if output_path:
        with open(output_path, "wb") as f:
            f.write(image_buffer.getvalue())

    return image_buffer


def do_print(
    name,
    receipt_type,
    student_id,
    paper_width,
    paper_height,
    dpi,
    printer_name=None,
    name_font_size_percent=0.15,
    receipt_font_size_percent=0.15,
    barcode_height_percent=0.55,
    margin_percent=0.03,
    code_moudle_width=0.3,
    code_moudle_height_percent=0.7,
    code_font_size_percent=0.225,
    code_text_distance_percent=0.3,
):
    image_width = int(0.9 * (paper_width / 25.4 * dpi))
    image_height = int(0.9 * (paper_height / 25.4 * dpi))
    image_buffer = generate_bmp(
        name=name,
        receipt_type=receipt_type,
        student_id=student_id,
        output_path=None,
        width=image_width,
        height=image_height,
        dpi=dpi,
        name_font_size_percent=name_font_size_percent,
        receipt_font_size_percent=receipt_font_size_percent,
        barcode_height_percent=barcode_height_percent,
        margin_percent=margin_percent,
        code_moudle_width=code_moudle_width,
        code_moudle_height_percent=code_moudle_height_percent,
        code_font_size_percent=code_font_size_percent,
        code_text_distance_percent=code_text_distance_percent,
    )
    # 转换BMP图像为TSPL指令
    tspl = bmp_to_tspl_bitmap(
        bmp=image_buffer,  # 替换为你的BMP文件路径或buffer
        x=calculate_x_position(paper_width, image_width, alignment="center"),  # X坐标
        y=calculate_y_position(paper_height, image_height, alignment="center"),  # Y坐标
        mode=0,  # 覆盖模式
    )

    # 完整标签示例（包含清除和打印指令）
    full_label = b"".join(
        [
            f"SIZE {paper_width} mm, {paper_height} mm\n".encode("ascii"),
            b"CLS\n",
            tspl,
            b"PRINT 1\n",
            b"END\n",
        ]
    )
    logger.debug(f"打印TSPL数据: \n{full_label}")
    print_tspl(
        full_label,
        printer_name=printer_name,
        doc_name=f"{student_id}-{name}-{receipt_type}",
    )


if __name__ == "__main__":
    PAPER_WIDTH = 70
    PAPER_HEIGHT = 140
    DPI = 203
    IMAGE_WIDTH = int(0.9 * (PAPER_WIDTH / 25.4 * DPI))
    IMAGE_HEIGHT = int(0.9 * (PAPER_HEIGHT / 25.4 * DPI))
    image_buffer = generate_bmp(
        name="星宇",
        receipt_type="学籍档案回执",
        student_id="24S051036",
        output_path=None,
        width=IMAGE_WIDTH,
        height=IMAGE_HEIGHT,
        dpi=DPI,
    )
    # 转换BMP图像为TSPL指令
    tspl = bmp_to_tspl_bitmap(
        bmp=image_buffer,  # 替换为你的BMP文件路径或buffer
        x=calculate_x_position(PAPER_WIDTH, IMAGE_WIDTH, alignment="center"),  # X坐标
        y=calculate_y_position(PAPER_HEIGHT, IMAGE_HEIGHT, alignment="center"),  # Y坐标
        mode=0,  # 覆盖模式
    )

    # 完整标签示例（包含清除和打印指令）
    full_label = b"".join(
        [
            f"SIZE {PAPER_WIDTH} mm, {PAPER_HEIGHT} mm\n".encode("ascii"),
            b"CLS\n",
            tspl,
            b"PRINT 1\n",
            b"END\n",
        ]
    )
    # print(full_label)
    # print_tspl(full_label)
